﻿using FreeSql;
using Microsoft.Extensions.DependencyInjection;
using Syspetro.Core;
using Syspetro.Core.DbAccessor;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace Syspetro.Deploy.FreeSqlDb
{
    public class FreeSqlRepository<T> : IRepository<T> where T : EntityBase, new()
    {
        public IFreeSql FreeSql;
        public FreeSqlRepository(IFreeSql freeSql)
        {
            FreeSql = freeSql;
        }
        private DbContext dbc;
        public DbContext DbC
        {
            get { if (dbc != null) { return dbc; } else { dbc = GetDbContext; return dbc; } }
        }
        public DbContext GetDbContext
        {
            get
            {
                return FreeSql.CreateDbContext();
            }
        }
        public int Save => DbC.SaveChanges();
        public Task<int> SaveAsync => DbC.SaveChangesAsync();

        public Guid Add(T data)
        {
            FreeSql.Insert(data);
            return data.Id;
        }
        public int Add(List<T> datas)
        {
            var db = GetDbContext;
            db.AddRange(datas);
            return db.SaveChanges();
        }
        public async Task<Guid> AddAsync(T data)
        {
            var db = GetDbContext;
            db.Add(data);
            await db.SaveChangesAsync();
            return data.Id;
        }
        public Task<int> AddAsync(List<T> datas)
        {
            var db = GetDbContext;
            db.AddRange(datas);
            return db.SaveChangesAsync();
        }
        public void AddTrans(T data)
        {
            DbC.Add(data);
        }
        public void AddTrans(List<T> datas)
        {
            DbC.AddRange(datas);
        }
        public bool Delete(Guid id)
        {
            var i = FreeSql.Delete<T>(new { Id = id }).ExecuteAffrows();
            return i > 0;
        }
        public bool Delete(T data)
        {
            return Delete(data.Id);
        }
        public int Delete(Expression<Func<T, bool>> expression)
        {
            return FreeSql.Delete<T>().Where(expression).ExecuteAffrows();
        }
        public async Task<bool> DeleteAsync(Guid id)
        {
            var i = await FreeSql.Delete<T>(new { Id = id }).ExecuteAffrowsAsync();
            return i > 0;
        }
        public Task<bool> DeleteAsync(T data)
        {
            return DeleteAsync(data.Id);
        }
        public Task<int> DeleteAsync(Expression<Func<T, bool>> expression)
        {
            return FreeSql.Delete<T>().Where(expression).ExecuteAffrowsAsync();
        }
        public void DeleteTrans(Guid id)
        {
            DbC.Remove(new T { Id = id });
        }
        public void DeleteTrans(T data)
        {
            DbC.Remove(data);
        }
        public void DeleteTrans(Expression<Func<T, bool>> expression)
        {
            DbC.Orm.Delete<T>().Where(expression).ExecuteAffrows();
        }
        public List<T> Find(Expression<Func<T, bool>> expression, string orderby = null, bool isAscending = true)
        {
            if (expression == null)
                return FreeSql.Select<T>().OrderByPropertyNameIf(!string.IsNullOrEmpty(orderby), orderby, isAscending).ToList();
            else
                return FreeSql.Select<T>().Where(expression).OrderByPropertyNameIf(!string.IsNullOrEmpty(orderby), orderby, isAscending).ToList();
        }
        public Task<List<T>> FindAsync(Expression<Func<T, bool>> expression, string orderby = null, bool isAscending = true)
        {
            if (expression == null)
                return FreeSql.Select<T>().OrderByPropertyNameIf(!string.IsNullOrEmpty(orderby), orderby, isAscending).ToListAsync();
            else
                return FreeSql.Select<T>().Where(expression).OrderByPropertyNameIf(!string.IsNullOrEmpty(orderby), orderby, isAscending).ToListAsync();
        }
        public T Get(Guid id)
        {
            return FreeSql.Select<T>().Where(t => t.Id == id).First();
        }
        public T Get(Expression<Func<T, bool>> expression)
        {
            return FreeSql.Select<T>().Where(expression).First();
        }
        public Task<T> GetAsync(Guid id)
        {
            return FreeSql.Select<T>().Where(t => t.Id == id).FirstAsync();
        }
        public Task<T> GetAsync(Expression<Func<T, bool>> expression)
        {
            return FreeSql.Select<T>().Where(expression).FirstAsync();
        }
        public bool Update(T date)
        {
            return FreeSql.Update<T>().SetSource(date).Where(t => t.Id == date.Id).ExecuteAffrows() > 0;
        }
        public async Task<bool> UpdateAsync(T date)
        {
            return await FreeSql.Update<T>().SetSource(date).Where(t => t.Id == date.Id).ExecuteAffrowsAsync() > 0;
        }
        public bool Update(T date, Expression<Func<T, object>> properties, bool isIgnore = false)
        {
            if (isIgnore)
            {
                return FreeSql.Update<T>().SetSource(date).Where(t => t.Id == date.Id).IgnoreColumns(properties).ExecuteAffrows() > 0;
            }
            else
            {
                return FreeSql.Update<T>().SetSource(date).Where(t => t.Id == date.Id).UpdateColumns(properties).ExecuteAffrows() > 0;
            }
        }
        public async Task<bool> UpdateAsync(T date, Expression<Func<T, object>> properties, bool isIgnore = false)
        {
            if (isIgnore)
            {
                return await FreeSql.Update<T>().SetSource(date).Where(t => t.Id == date.Id).IgnoreColumns(properties).ExecuteAffrowsAsync() > 0;
            }
            else
            {
                return await FreeSql.Update<T>().SetSource(date).Where(t => t.Id == date.Id).UpdateColumns(properties).ExecuteAffrowsAsync() > 0;
            }
        }
        public int Updates(T data, Expression<Func<T, bool>> expression, Expression<Func<T, object>> properties, bool isIgnore = false)
        {
            if (isIgnore)
            {
                return FreeSql.Update<T>().SetSource(data).Where(expression).IgnoreColumns(properties).ExecuteAffrows();
            }
            else
            {
                return FreeSql.Update<T>().SetSource(data).Where(expression).UpdateColumns(properties).ExecuteAffrows();
            }
        }
        public Task<int> UpdateAsync(T data, Expression<Func<T, bool>> expression, Expression<Func<T, object>> properties, bool isIgnore = false)
        {
            if (isIgnore)
            {
                return FreeSql.Update<T>().SetSource(data).Where(expression).IgnoreColumns(properties).ExecuteAffrowsAsync();
            }
            else
            {
                return FreeSql.Update<T>().SetSource(data).Where(expression).UpdateColumns(properties).ExecuteAffrowsAsync();
            }
        }
        public void UpdateTrans(T data)
        {
            DbC.Set<T>().Update(data);
        }
        public void UpdateTrans(T data, Expression<Func<T, object>> properties, bool isIgnore = false)
        {
            if (isIgnore)
            {
                DbC.Orm.Update<T>().SetSource(data).IgnoreColumns(properties).Where(t => t.Id == data.Id).ExecuteAffrows();
            }
            else
            {
                DbC.Orm.Update<T>().SetSource(data).UpdateColumns(properties).Where(t => t.Id == data.Id).ExecuteAffrows();
            }
        }
        public List<T> FindPage(out int total, Expression<Func<T, bool>> expression, string orderby, int current, int size, bool isAscending = true)
        {
            List<T> list;
            if (orderby == null)
            {
                list = FreeSql.Select<T>().Where(expression).Count(out var _total).Page(current, size).ToList();
                total = (int)_total;
            }
            else
            {
                if (isAscending)
                {
                    list = FreeSql.Select<T>().Where(expression).OrderByPropertyName(orderby).Count(out var _total).Page(current, size).ToList();
                    total = (int)_total;
                }
                else
                {
                    list = FreeSql.Select<T>().Where(expression).OrderByPropertyName(orderby, false).Count(out var _total).Page(current, size).ToList();
                    total = (int)_total;
                }
            }
            return list;
        }
        public List<V> FindPageDto<V>(out int total, Expression<Func<T, bool>> expression, string orderby, int current, int size, bool isAscending = true) where V : class, new()
        {
            List<V> list;
            if (orderby == null)
            {
                list = FreeSql.Select<T>().Where(expression).Count(out var _total).Page(current, size).ToList<V>();
                total = (int)_total;
            }
            else
            {
                if (isAscending)
                {
                    list = FreeSql.Select<T>().Where(expression).OrderByPropertyName(orderby).Count(out var _total).Page(current, size).ToList<V>();
                    total = (int)_total;
                }
                else
                {
                    list = FreeSql.Select<T>().Where(expression).OrderByPropertyName(orderby, false).Count(out var _total).Page(current, size).ToList<V>();
                    total = (int)_total;
                }
            }
            return list;
        }
        public int ExecuteNonQuery(string sql, DbParameter[] pars)
        {
            return FreeSql.Ado.ExecuteNonQuery(CommandType.Text, sql, pars);
        }
        public DataTable ExecuteSelect(string sql, DbParameter[] pars)
        {
            return FreeSql.Ado.ExecuteDataTable(CommandType.Text, sql, pars);
        }
        public Task<DataTable> ExecuteSelectAsync(string sql, DbParameter[] pars)
        {
            return FreeSql.Ado.ExecuteDataTableAsync(CommandType.Text, sql, pars);
        }
        public Task<int> ExecuteNonQueryAsync(string sql, DbParameter[] pars)
        {
            return FreeSql.Ado.ExecuteNonQueryAsync(CommandType.Text, sql, pars);
        }
        public DataTable ExecuteSelectObject(string sql, object pars)
        {
            return ExecuteSelect(sql, FreeSql.Ado.GetDbParamtersByObject(pars));
        }
        public int ExecuteNonQueryObject(string sql, object pars)
        {
            return ExecuteNonQuery(sql, FreeSql.Ado.GetDbParamtersByObject(pars));
        }
        public Task<DataTable> ExecuteSelectObjectAsync(string sql, object pars)
        {
            return ExecuteSelectAsync(sql, FreeSql.Ado.GetDbParamtersByObject(pars));
        }
        public Task<int> ExecuteNonQueryObjectAsync(string sql, object pars)
        {
            return ExecuteNonQueryAsync(sql, FreeSql.Ado.GetDbParamtersByObject(pars));
        }
        public V GetDto<V>(Guid id) where V : class, new()
        {
            return FreeSql.Select<T>().Where(t => t.Id == id).ToOne<V>();
        }
        public Task<V> GetDtoAsync<V>(Guid id) where V : class, new()
        {
            return FreeSql.Select<T>().Where(t => t.Id == id).ToOneAsync<V>();
        }
        public V GetDto<V>(Expression<Func<T, bool>> expression) where V : class, new()
        {
            return FreeSql.Select<T>().Where(expression).ToOne<V>();
        }
        public Task<V> GetDtoAsync<V>(Expression<Func<T, bool>> expression) where V : class, new()
        {
            return FreeSql.Select<T>().Where(expression).ToOneAsync<V>();
        }
        public List<V> FindDto<V>(Expression<Func<T, bool>> expression, string orderby = null, bool isAscending = true) where V : class, new()
        {
            if (expression == null)
                return FreeSql.Select<T>().OrderByPropertyNameIf(!string.IsNullOrEmpty(orderby), orderby, isAscending).ToList<V>();
            else
                return FreeSql.Select<T>().Where(expression).OrderByPropertyNameIf(!string.IsNullOrEmpty(orderby), orderby, isAscending).ToList<V>();
        }
        public Task<List<V>> FindDtoAsync<V>(Expression<Func<T, bool>> expression, string orderby = null, bool isAscending = true) where V : class, new()
        {
            if (expression == null)
                return FreeSql.Select<T>().OrderByPropertyNameIf(!string.IsNullOrEmpty(orderby), orderby, isAscending).ToListAsync<V>();
            else
                return FreeSql.Select<T>().Where(expression).OrderByPropertyNameIf(!string.IsNullOrEmpty(orderby), orderby, isAscending).ToListAsync<V>();
        }
        public int UpdateDto<V>(V data, Expression<Func<T, bool>> expression) where V : new()
        {
            return FreeSql.Update<T>().SetDto(data).Where(expression).ExecuteAffrows();
        }
        public Task<int> UpdateDtoAsync<V>(V data, Expression<Func<T, bool>> expression) where V : new()
        {
            return FreeSql.Update<T>().SetDto(data).Where(expression).ExecuteAffrowsAsync();
        }
        public void AddTrans<U>(U data) where U : EntityBase, new()
        {
            DbC.Add(data);
        }
        public void AddTrans<U>(List<U> datas) where U : EntityBase, new()
        {
            DbC.AddRange(datas);
        }
        public void DeleteTrans<U>(U data) where U : EntityBase, new()
        {
            DbC.Set<U>().Remove(data);
        }
        public void DeleteTrans<U>(Expression<Func<U, bool>> expression) where U : EntityBase, new()
        {
            DbC.Orm.Delete<U>().Where(expression).ExecuteAffrows();
        }
        public void UpdateTrans<U>(U data) where U : EntityBase, new()
        {
            DbC.Set<U>().Update(data);
        }
        public void UpdateTrans<U>(U data, Expression<Func<U, object>> properties, bool isIgnore = false) where U : EntityBase, new()
        {
            if (isIgnore)
            {
                DbC.Orm.Update<U>().SetSource(data).IgnoreColumns(properties).Where(t => t.Id == data.Id).ExecuteAffrows();
            }
            else
            {
                DbC.Orm.Update<U>().SetSource(data).UpdateColumns(properties).Where(t => t.Id == data.Id).ExecuteAffrows();
            }
        }
        public bool Any(Expression<Func<T, bool>> expression)
        {
            return FreeSql.Select<T>().Where(expression).Any();
        }
        public Task<bool> AnyAsync(Expression<Func<T, bool>> expression)
        {
            return FreeSql.Select<T>().Where(expression).AnyAsync();
        }
    }
}
